//
// Created by cljh on 2023/3/28.
//

#ifndef BIN_PACKING_PLACEMENT_H
#define BIN_PACKING_PLACEMENT_H


#include <map>
#include <vector>
#include "../entity/NestPath.h"
#include "../entity/PolygonList.h"
#include "../entity/Point.h"
#include "../entity/Polygon.h"
#include "../utils/GeometryUtils.h"
#include "../entity/NfpKey.h"
#include "../utils/ClipperUtil.h"

#define _USE_MATH_DEFINES
#include <cmath>
#include <iostream>

class Placement{
public:
    NestPath bin;
    std::map<std::string,NestPath>* nfpCache;

    Placement(NestPath _bin,std::map<std::string,NestPath>* _nfpCache){
        bin=_bin;
        nfpCache=_nfpCache;
    }

    bool placePolygons(PolygonList polygonList,std::vector<Vector> &retPlacements,double &retFitness, double &rectWidth){
        double fitness=0;

        PolygonList polygonsRotated=PolygonList();
        for(int i=0;i<polygonList.size();i++){
            NestPath polygonRotated=NestPath();
            int degree=polygonList.getRotate(i);
            NestPath polygon=polygonList.getPolygon(i);
            for(int j=0;j<polygon.size();j++){
                Polygon* path=polygon[j];
                path=GeometryUtils::rotatePolygon(path,degree);
                polygonRotated.push_back(path);
            }
            polygonRotated.id=polygon.id;
            polygonRotated.rotation=polygon.rotation;
            polygonsRotated.push_back(polygonRotated);
        }
        polygonList=polygonsRotated;

        //记录摆放的位移向量
        std::vector<std::vector<Vector>> placementVector=std::vector<std::vector<Vector>>();

        //        while(polygonList.size>0){
        //开启一张新的底板
        PolygonList placedList=PolygonList();
        std::vector<Vector> placements=std::vector<Vector>();

        //记录之前摆放的外边框
        double left=DBL_MAX,right=-DBL_MAX;
        double top=-DBL_MAX,bottom=DBL_MAX;

        for(int i=0;i<polygonList.size();i++){
            NestPath nestPath=polygonList.getPolygon(i);
            int id=polygonList.getId(i);
            int angle=polygonList.getRotate(i);
            NfpKey key=NfpKey(0,id, true,0,angle);
            //get ifp
            std::string mapKey=key.toString();
            NestPath ifp;
            if(nfpCache->count(mapKey)>0){
                ifp=nfpCache->at(mapKey);
            }else{
                std::cout<<"cant find ifp,key is: "+mapKey<<std::endl;
                return false;
            }

            Vector position=Vector(DBL_MAX,DBL_MAX);

            double left2p=-DBL_MAX,right2p=-DBL_MAX;
            double top2p=-DBL_MAX,bottom2p=-DBL_MAX;
            //找到图形外边框对于它自己参考点的最大距离
            Point referencePoint=nestPath[0]->getPoint(0);
            for(int j=0;j<nestPath.size();j++){
                Polygon *polygon=nestPath[j];
                for(int k=0;k<polygon->lenth;k++){
                    Point p=polygon->getPoint(k);

                    if(p.x-referencePoint.x>right2p){
                        right2p=p.x-referencePoint.x;
                    }
                    if(referencePoint.x-p.x>left2p){
                        left2p=referencePoint.x-p.x;
                    }
                    if(p.y-referencePoint.y>top2p){
                        top2p=p.y-referencePoint.y;
                    }
                    if(referencePoint.y-p.y>bottom2p){
                        bottom2p=referencePoint.y-p.y;
                    }
                }
            }
            if(left2p<0){
                left2p=0;
            }
            if(right2p<0){
                right2p=0;
            }
            if(top2p<0){
                top2p=0;
            }
            if(bottom2p<0){
                bottom2p=0;
            }

            if(placedList.size()==0){
                // first placement , put it on the left
                //找一个ifp中最靠左的摆放点
                //记录四周极值
                for(int j=0;j<ifp.size();j++){
                    Polygon *polygon=ifp[j];
                    for(int k=0;k<polygon->lenth;k++){
                        Point p=polygon->getPoint(k);
                        if(p.x==position.x){
                            if(p.y<position.y){
                                position=p;
                            }
                        }
                        if(p.x<position.x){
                            position=p;
                        }
                    }
                }
                placements.push_back(position);
                placedList.push_back(nestPath);//加入的是已经旋转过的，-rotation旋转即可得到旋前图形

                if(position.x+right2p>right){
                    right=position.x+right2p;
                }
                if(position.x-left2p<left){
                    left=position.x-left2p;
                }
                if(position.y+top2p>top){
                    top=position.y+top2p;
                }
                if(position.y-bottom2p<bottom){
                    bottom=position.y-bottom2p;
                }
                continue;
            }
            std::vector<NestPath> nestPaths=std::vector<NestPath>();
            //获取所有已经摆放的nfp，需要加上摆放的向量
            for(int j=0;j<placedList.size();j++){
                int pid=placedList.getId(j);
                int pangle=placedList.getRotate(j);
                NfpKey nfpKey=NfpKey(pid,id, false,pangle,angle);
                std::string nfpString=nfpKey.toString();
                if(nfpCache->count(nfpString)>0){
                    NestPath nfp=nfpCache->at(nfpString);
//                    for(int k=0;k<nfp.size();k++){
                        //将所有子图形添加进clipper进行union操作
                        nestPaths.push_back(nfp);
//                    }
                }else{
                    std::cout<<"无法获取nfp,key is: "+nfpString<<std::endl;
                        for(int m=0;m<placedList.size();m++){
                        NestPath path=placedList.getPolygon(m);
                        path.selfDelete();
                    }
                    return false;
                }
            }

            //使用clipper将nfp合成
            NestPath combinedNfp=ClipperUtil::unionPolygons(nestPaths,placements);


            //合成之后使用difference操作，将底板ifp中不含nfp的部分表示
            //TODO:如果大小正好嵌合，则Clipper结果非最佳摆放点
            NestPath placeable=ClipperUtil::differencePolygons(ifp,combinedNfp);

            //摆放在面积最小的位置上
            //需要的变量为：摆放过程中最右侧的坐标的x和y，最小摆放面积
            //维护包含图形的外边框的坐标

            double area=DBL_MAX;
            Vector placementVector;
            for(int j=0;j<placeable.size();j++){
                Polygon *polygon=placeable[j];
                for(int k=0;k<polygon->lenth;k++){
                    //遍历每一个可以摆放的位置，找到外边框面积（大概是）的最小值
                    Point p=polygon->getPoint(k);
//                    double boundleft=left;
//                    if(p.x-left2p<boundleft){
//                        boundleft=p.x-left2p;
//                    }
//                    double boundright=right;
//                    if(p.x+right2p>boundright){
//                        boundright=p.x+right2p;
//                    }
//                    double boundtop=top;
//                    if(p.y+top2p>boundtop){
//                        boundtop=p.y+top2p;
//                    }
//                    double boundbottom=bottom;
//                    if(p.y-bottom2p<boundbottom){
//                        boundbottom=p.y-bottom2p;
//                    }

//                    //这个计算比较关键
//                    double boundArea=(boundtop-boundbottom)+2*(boundright-boundleft);

//                    if(boundArea<area){
//                        area=boundArea;
//                        placementVector=p;
//                    }
//                     纯左底
                    if(p.x<area){
                        area = p.x;
                        placementVector = p;
                    }

                }
            }

            if(placementVector.x+right2p>right){
                right=placementVector.x+right2p;
            }
            if(placementVector.x-left2p<left){
                left=placementVector.x-left2p;
            }
            if(placementVector.y+top2p>top){
                top=placementVector.y+top2p;
            }
            if(placementVector.y-bottom2p<bottom){
                bottom=placementVector.y-bottom2p;
            }

            placements.push_back(placementVector);
            placedList.push_back(nestPath);

            placeable.selfDelete();
            combinedNfp.selfDelete();
        }

        double binleft=DBL_MAX,binright=-DBL_MAX;
        for(int i=0;i<bin.size();i++){
            Polygon * p=bin[i];
            for(int j=0;j<p->lenth;j++){
                Point point=p->getPoint(j);
                if(point.x<binleft){
                    binleft=point.x;
                }
                if(point.x>binright){
                    binright=point.x;
                }
            }
        }
        fitness+=(binright-binleft)/(right-left);
        //        }

        retFitness=fitness;
        retPlacements=placements;
        rectWidth = right-left;

        for(int i=0;i<placedList.size();i++){
            NestPath path=placedList.getPolygon(i);
            path.selfDelete();
        }

        return true;
    }
};


#endif //BIN_PACKING_PLACEMENT_H
